package com.yeung;

import com.yeung.bean.Department;
import com.yeung.bean.Employee;
import com.yeung.mapper.DepartmentMapper;
import com.yeung.mapper.EmployeeMapper;
import com.yeung.mapper.EmployeeMapperPlus;
import com.yeung.mapper.annotation.EmployeeMapperAnnotation;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * mybatis-config.xml是全局配置文件,包裹数据库连接池信息和事务等等
 * mapper映射文件 (必须要有),映射sql
 */
public class MybatisTest {
    private static SqlSessionFactory sessionFactory;
    private static SqlSession session;

    static {
        InputStream resourceAsStream = null;
        try {
            //拿到配置文件的数据流
            resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
        } catch (IOException e) {
            System.out.println("找不到这个配置文件");
        }
        //创建 sessionFactory
        sessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    }

    public static SqlSession getSession() {
        if (session == null)
            session = sessionFactory.openSession();
        return session;
    }

    @Test
    public void test() {
        //创建session对象 操作sql
        SqlSession session = getSession();
        //第一种调用方法
        Employee e = session.selectOne("com.yeung.mapper.EmployeeMapper.getEmpById", 1);
        System.out.println(e);


    }

    @Test
    public void test2() {
        //第二种 !! 推荐使用这个版本
        //而是采用接口的方案, 使用mybatis的代理对象
        SqlSession session = getSession();
        EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);//拿到接口的代理对象
        Employee emp = mapper.getEmpById(1);
        System.out.println(emp);
    }

    /**
     * 测试使用注解写的mapper
     * EmployeeMapperAnnotation
     */
    @Test
    public void test3() {
        SqlSession session = getSession();
        EmployeeMapperAnnotation mapper = session.getMapper(EmployeeMapperAnnotation.class);//拿到接口的代理对象
        Employee emp = mapper.getEmpByid(1);
        System.out.println(emp);
    }

    /**
     * 多参数查询测试
     */
    @Test
    public void test4() {
        SqlSession session = getSession();
        EmployeeMapperAnnotation mapper = session.getMapper(EmployeeMapperAnnotation.class);//拿到接口的代理对象
        Employee emp = mapper.getEmpById_Name(1, "john");
        System.out.println(emp);
    }

    /**
     * 多参数查询测试2
     */
    @Test
    public void test5() {
        SqlSession session = getSession();
        EmployeeMapperAnnotation mapper = session.getMapper(EmployeeMapperAnnotation.class);//拿到接口的代理对象
        Map<String, Object> param = new HashMap<>();
        param.put("id", 1);
        param.put("name", "john");
        Employee emp = mapper.getEmpByMap(param);
        System.out.println(emp);
    }

    @Test
    public void testInnerParam() {
        SqlSession session = getSession();
        EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);//拿到接口的代理对象
        List<Employee> employees = mapper.getEmpsTestInnerParameter(null);
        System.out.println(employees);
    }

    @Test
    public void delete() {
        //第二种 !! 推荐使用这个版本
        //而是采用接口的方案, 使用mybatis的代理对象
        SqlSession session = getSession();
        EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);//拿到接口的代理对象
        mapper.deleteEmp(2);
        session.commit();
    }

    @Test
    public void addEmp() {
        //第二种 !! 推荐使用这个版本
        //而是采用接口的方案, 使用mybatis的代理对象
        SqlSession session = getSession();
        EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);//拿到接口的代理对象
        boolean b = mapper.addEmp(new Employee(null, "john", "dksls@gmail.com", "男"));
        System.out.println(b);
        session.commit();//open拿到的session不会自动提交
    }

    @Test
    public void updateEmp() {
        //第二种 !! 推荐使用这个版本
        //而是采用接口的方案, 使用mybatis的代理对象
        SqlSession session = getSession();
        EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);//拿到接口的代理对象
        mapper.updateEmp(new Employee(1, "john", "dksls@gmail.com", "女"));
        session.commit();
    }

    @Test
    public void 自定义返回数据类型测试() {
        SqlSession session = getSession();
        EmployeeMapperPlus mapper = session.getMapper(EmployeeMapperPlus.class);
        Employee empAndDept = mapper.getEmpAndDept(4);
        System.out.println(empAndDept);
    }

    @Test
    public void 自定义返回数据类型分步查询测试() {
        SqlSession session = getSession();
        EmployeeMapperPlus mapper = session.getMapper(EmployeeMapperPlus.class);
        Employee empAndDept = mapper.getEmpAndDeptStep(4);
        System.out.println(empAndDept);
    }

    @Test
    public void 自定义关联查询测试() {
        SqlSession session = getSession();
        DepartmentMapper mapper = session.getMapper(DepartmentMapper.class);
        Department department = mapper.getDeptByIdPlus(2);
        System.out.println(department.getEmps());
    }

    @Test
    public void 自定义关联分步查询测试() {
        SqlSession session = getSession();
        DepartmentMapper mapper = session.getMapper(DepartmentMapper.class);
        Department department = mapper.getDeptByIdPlusStep(2);
//        System.out.println(department.getEmps());
    }

    /**
     * 缓存可以极大的提升查询效率
     * mybatis系统中默认定义了两级缓存
     * 一级缓存和二级缓存
     * 1. 默认情况下,只有以及一级(sqlsession级别的缓存,也称为本地缓存)开启
     * 2. 二级缓存(全局缓存)需要手动开启和配置,他是基于namspacke级别的缓存
     * 3. 为了提高扩展性,mybatis定义了缓存接口cache,我们可以通过实现cache接口来自定义二级缓存
     */
    @Test
    public void testCache() {
        SqlSession session = getSession();
        EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
        Employee e1 = mapper.getEmpById(1);
        System.out.println(e1.hashCode());
        Employee e2 = mapper.getEmpById(1);
        System.out.println(e2.hashCode());
        //是同一个对象,第二次没有实际去查数据库
        //一级缓存失效了(session不同,失效了,关闭了,清空了),才回去重新查询
        System.out.println(e1 == e2);

    }

    /**
     * 基于namespace级别的缓存:一个namespacke对应一个二级缓存(不同namespace查出的数据会放在自己的对应的缓存中)
     * 工作机制:
     * 1. 一个会话,查询一条数据,这个数据就会被放在当前会发的一级缓存中,
     * 2. 如果会话关闭,一级缓存中的数据会被保存到二级缓存中,新的会话查询信息,就可以参照二级缓存
     * !!!增删改默认的flushCache=true, 执行完sql会清空一二级缓存
     */
    @Test
    public void testCache2() {
        SqlSession session = sessionFactory.openSession();
        SqlSession session2 = sessionFactory.openSession();

        EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);

        EmployeeMapper mapper2 = session2.getMapper(EmployeeMapper.class);

        Employee e1 = mapper.getEmpById(1);
        session.close();
        //
        //必须是一级缓存关闭了才会将一级缓存中的数据转移到二级缓冲中区
        Employee e2 = mapper2.getEmpById(1);

        System.out.println(e1 == e2);//从二级缓存中序列化克隆的一个对象,但是数据是一样的
        session2.close();


    }

    @After
    public void close() {
        if (session != null)
            session.close();
    }


}
